meta-values
,是用来用算法描述 ECMAScript 语言结构和 ECMAScript 语言类型的,包括Reference
、 Lexical Environment
和 Environment Record
src\Reference.js
const EnvironmentRecord = require('./EnvironmentRecord');
class Reference {
constructor(base, name, strict) {
this.base = base;
this.name = name;
this.strict = strict;
}
static GetBase(V) {
return V.base;
}
static GetReferencedName(V) {
return V.name;
}
static IsStrictReference(V) {
return V.strict;
}
static HasPrimitiveBase(V) {
return V.base.toString() === `[Object Boolean]`
|| V.base.toString() === `[Object String]`
|| V.base.toString() === `[Object Number]`
}
static IsPropertyReference(V) {
return (typeof V.base === 'object' && (!(V.base instanceof EnvironmentRecord))) || Reference.HasPrimitiveBase(V)
}
static IsUnresolvableReference(V) {
return typeof V.base === 'undefined';
}
static GetValue(V) {
//如果 Type(V) 不是引用 , 返回 V。
if (!Reference.Type(V)) return V;
//令 base 为调用 GetBase(V) 的返回值
let base = Reference.GetBase(V);
//如果 IsUnresolvableReference(V), 抛出一个 ReferenceError 异常。
if (Reference.IsUnresolvableReference(V)) {
throw new Error(`ReferenceError ${V} is not defined`);
}
//如果 IsPropertyReference(V),
if (Reference.IsPropertyReference(V)) {
//如果 HasPrimitiveBase(V) 是 false,
if (!Reference.HasPrimitiveBase(V)) {
let name = Reference.GetReferencedName(V);
return base[name];
} else {
let O = Reference.ToObject(base);
let name = Reference.GetReferencedName(V);
let desc = Object.getOwnPropertyDescriptor(O, name);
if (typeof desc === 'undefined') return undefined;
if (Reference.IsDataDescriptor(desc)) {
return desc.value;
} else if (IsAccessorDescriptor(desc)) {
let getter = desc.get;
if (typeof getter === 'undefined') return undefined;
return getter.call(base);
}
}
} else {
//否则 , base 必须是一个 environment record
//传递 GetReferencedName(V) 和 IsStrictReference(V) 为参数调用 base 的 GetBindingValue( 见 10.2.1) 具体方法,返回结果。
let name = Reference.GetReferencedName(V);
let strict = Reference.IsStrictReference(V);
base.GetBindingValue(name, strict);
}
return base(Reference.GetReferencedName(V));
}
static IsDataDescriptor(desc) {
return desc.value || desc.writable;
}
static IsAccessorDescriptor(desc) {
return desc.get || desc.set;
}
static ToObject(PrimitiveValue) {
let type = Object.prototype.toString.call(PrimitiveValue).slice(8, -1);
switch (type) {
case 'Boolean':
return new Boolean(PrimitiveValue);;
case 'Number':
return new Number(PrimitiveValue);
case 'String':
return new String(PrimitiveValue);
default:
break;
}
}
static Type(V) {
return V instanceof Reference;
}
}
module.exports = Reference;
src\3.this.js
//console.log(obj.getName());
//令 ref 为解释执行 MemberExpression 的结果
var name = 1;
var obj = {
name: 2,
getName: function () {
return this.name;
}
}
console.log(obj.getName());
/*
console.log((obj.getName)());
console.log((obj.getName = obj.getName)());
console.log((true && obj.getName)());
console.log((obj.getName, obj.getName)());
console.log((true && obj.getName)()); */
const Reference = require('./Reference');
let ref = new Reference(obj, 'getName', false);
//let ref = obj.getName;
//令 func 为 GetValue(ref).
let func = Reference.GetValue(ref);
//令 argList 为解释执行 Arguments 的结果 , 产生参数值们的内部列表 (see 11.2.4).
let argList = [];
//如果 Type(func) is not Object ,抛出一个 TypeError 异常 .
/* if (!Type(func)) {
throw new Error(`TypeError`);
} */
//如果 IsCallable(func) is false ,抛出一个 TypeError 异常 .
/* if (!IsCallable(func)) {
throw new Error(`TypeError`);
} */
//如果 Type(ref) 为 Reference,那么 如果 IsPropertyReference(ref) 为 true,
//那么 令 thisValue 为 GetBase(ref).
//否则, ref 的基值是一个环境记录项 令 thisValue 为调用 GetBase(ref) 的 ImplicitThisValue 具体方法的结果
let thisValue;
if (Reference.Type(ref)) {
if (Reference.IsPropertyReference(ref)) {
thisValue = Reference.GetBase(ref);
} else {
thisValue = Reference.GetBase(ref).ImplicitThisValue();
}
} else {
//否则 , 假如 Type(ref) 不是 Reference. 令 thisValue 为 undefined.
thisValue = undefined;
}
//返回调用 func 的 [[Call]] 内置方法的结果 , 传入 thisValue 作为 this 值和列表 argList 作为参数列表
let result = func.call(thisValue, ...argList);
console.log(result);
//当用一个 this 值,一个参数列表调用函数对象 F 的 [[Call]] 内部方法,采用以下步骤:
const ExecutionContext = require('./ExecutionContext');
const LexicalEnvironment = require('./LexicalEnvironment');
const FunctionDeclaration = require('./FunctionDeclaration');
const Reference = require('./Reference');
const ECStack = require('./ECStack');
function sum(a, b) {
console.log(this, a, b);
}
//sum();
let fn = 'sum';
let FormalParameterList = "a,b";
let FunctionBody = `
console.log(this, a, b);
`;
const globalLexicalEnvironment = LexicalEnvironment.NewObjectEnvironment(global, null);
const globalEC = new ExecutionContext(globalLexicalEnvironment, global);
ECStack.push(globalEC);
let env = ECStack.current.lexicalEnvironment.environmentRecord;
let Scope = ECStack.current.lexicalEnvironment;
let fo = FunctionDeclaration.createInstance(fn, FormalParameterList, FunctionBody, Scope);
let ref = new Reference(env, 'sum', false);
let thisValue;
if (Reference.Type(ref)) {
if (Reference.IsPropertyReference(ref)) {
thisValue = Reference.GetBase(ref);
} else {
thisValue = Reference.GetBase(ref).ImplicitThisValue();
}
} else {
//否则 , 假如 Type(ref) 不是 Reference. 令 thisValue 为 undefined.
thisValue = undefined;
}
let FormalParameters = FormalParameterList.split(',');
let localEnv = LexicalEnvironment.NewDeclarativeEnvironment(fo[`[[Scope]]`]);
let funcCtx = new ExecutionContext(localEnv, thisValue);
ECStack.push(funcCtx);
env = ECStack.current.lexicalEnvironment.environmentRecord;
let strict = false;
let args = [1, 2];
//令 argCount 为 args 中元素的数量
let argCount = args.length;
let n = 0;
FormalParameters.forEach(argName => {
n += 1;
let v = n > argCount ? undefined : args[n - 1];
argAlreadyDeclared = env.HasBinding(argName);
if (!argAlreadyDeclared) {
env.CreateMutableBinding(argName);
}
env.SetMutableBinding(argName, v, strict);
});
console.log(
thisValue,
Reference.GetValue(
LexicalEnvironment.GetIdentifierReference(ECStack.current.lexicalEnvironment, 'a')),
Reference.GetValue(
LexicalEnvironment.GetIdentifierReference(ECStack.current.lexicalEnvironment, 'b'))
);
ECStack.pop();
//Function.prototype.call (thisArg [ , arg1 [ , arg2, … ] ] )
function sum(a, b) {
console.log(this, a, b);
}
let thisArg = { name: 'obj' };
sum.call(thisArg, 1, 2);
//当以 thisArg 和可选的 arg1, arg2 等等作为参数在一个 func 对象上调用 call 方法,采用如下步骤:
//如果 IsCallable(func) 是 false, 则抛出一个 TypeError 异常。
let func = sum;
/* if (!IsCallable(func)) {
throw new Error(`TypeError`);
} */
//令 argList 为一个空列表。
let argList = [];
//如果调用这个方法的参数多余一个,则从 arg1 开始以从左到右的顺序将每个参数插入为 argList 的最后一个元素。
argList.push(1);
argList.push(2);
//提供 thisArg 作为 this 值并以 argList 作为参数列表,调用 func 的 [[Call]] 内部方法,返回结果。
console.log(
thisArg
);
//Function.prototype.apply (thisArg, argArray)
const { Type } = require('./utils');
function func(a, b) {
console.log(this, a, b);
}
let thisArg = { name: 'obj' };
func.apply(thisArg, [1, 2]);
//当以 thisArg 和 argArray 为参数在一个 func 对象上调用 apply 方法,采用如下步骤:
//如果 IsCallable(func) 是 false, 则抛出一个 TypeError 异常 .
/* if (!IsCallable(func)) {
throw new Error(`TypeError`);
} */
//如果 argArray 是 null 或 undefined,
//则返回提供 thisArg 作为 this 值并以空参数列表调用 func 的 [[Call]] 内部方法的结果。
let argArray = [1, 2];
if (!argArray) {
console.log(thisArg, undefined, undefined);
}
//如果 Type(argArray) 不是 Object, 则抛出一个 TypeError 异常 .
if (Type(argArray) !== 'object') {
throw new Error(`TypeError`);
}
//令 len 为以 "length" 作为参数调用 argArray 的 [[Get]] 内部方法的结果。
let len = argArray.length;
//令 n 为 ToUint32(len).
let n = len;
//令 argList 为一个空列表 .
let argList = [];
let index = 0;
while (index < n) {
let indexName = index.toString();
let nextArg = argArray[indexName];
argList.push(nextArg);
index = index + 1;
}
//提供 thisArg 作为 this 值并以 argList 作为参数列表,调用 func 的 [[Call]] 内部方法,返回结果。
// Function.prototype.bind (thisArg [, arg1 [, arg2, …]])
// bind 方法需要一个或更多参数,thisArg 和(可选的)arg1, arg2, 等等,执行如下步骤返回一个新函数对象:
function func(a, b) {
console.log(this, a, b);
}
let thisArg = { name: 'obj' };
let newFunc = func.bind(thisArg, 1);
newFunc(2);
const { IsCallable } = require('./utils');
//令 Target 为 this 值 .
let Target = thisArg;
//如果 IsCallable(Target) 是 false, 抛出一个 TypeError 异常 .
func[`[[call]]`] = true;
if (!IsCallable(func)) {
throw new Error(`TypeError`);
}
//令 A 为一个(可能为空的)新内部列表,它包含按顺序的 thisArg 后面的所有参数(arg1, arg2 等等)。
let A = [1];
//令 F 为一个新原生 ECMAScript 对象。
let F = {};
//依照 8.12 指定,设定 F 的除了 [[Get]] 之外的所有内部方法。
//依照 15.3.5.4 指定,设定 F 的 [[Get]] 内部属性。
//设定 F 的 [[TargetFunction]] 内部属性为 Target。
F[`[[TargetFunction]]`] = Target;
//设定 F 的 [[BoundThis]] 内部属性为 thisArg 的值。
F[`[[BoundThis]]`] = thisArg;
//设定 F 的 [[BoundArgs]] 内部属性为 A。
F[`[[BoundArgs]]`] = A;
//设定 F 的 [[Class]] 内部属性为 "Function"。
F[`[[Class]]`] = 'Function';
//设定 F 的 [[Prototype]] 内部属性为 15.3.3.1 指定的标准内置 Function 的 prototype 对象。
F[`[[Prototype]]`] = Function.prototype;
//依照 15.3.4.5.1 描述,设定 F 的 [[Call]] 内置属性。
//依照 15.3.4.5.2 描述,设定 F 的 [[Construct]] 内置属性。
//依照 15.3.4.5.3 描述,设定 F 的 [[HasInstance]] 内置属性。
//如果 Target 的 [[Class]] 内部属性是 "Function", 则
//令 L 为 Target 的 length 属性减 A 的长度。
//设定 F 的 length 自身属性为 0 和 L 中更大的值。
if (Target[`[[Class]]`] === 'Function') {
let L = Target.length - A.length;
F.length = Math.max(0, L);
} else {
//否则设定 F 的 length 自身属性为 0.
F.length = 0;
}
//设定 F 的 length 自身属性的特性为 15.3.5.1 指定的值。
//设定 F 的 [[Extensible]] 内部属性为 true。
F[`[[Extensible]] `] = true;
//令 thrower 为 [[ThrowTypeError]] 函数对象 (13.2.3)。
let thrower = (key, value) => { };
//以 "caller", 属性描述符 {[[Get]]: thrower, [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: - false}, 和 false 作为参数调用 F 的 [[DefineOwnProperty]] 内部方法。
Object.defineProperty(F, 'caller', {
get: thrower,
set: thrower,
enumerable: false,
configurable: false
});
//以 "arguments", 属性描述符 {[[Get]]: thrower, [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: false}, 和 false 作为参数调用 F 的 [[DefineOwnProperty]] 内部方法。
Object.defineProperty(F, 'arguments', {
get: thrower,
set: thrower,
enumerable: false,
configurable: false
});
//bind 方法的 length 属性是 1。
//Function.prototype.bind 创建的函数对象不包含 prototype 属性或 [[Code]], [[FormalParameters]], [[Scope]] 内部属性
// 当调用一个用 bind 函数创建的函数对象 F 的 [[Call]] 内部方法,传入一个 this 值和一个参数列表 ExtraArgs,采用如下步骤:
let boundArgs = F[`[[BoundArgs]]`];
let boundThis = F[`[[BoundThis]]`];
let target = F[`[[TargetFunction]]`];
//令 args 为一个新列表,它包含与列表 boundArgs 相同顺序相同值,后面跟着与 ExtraArgs 是相同顺序相同值。
let ExtraArgs = [2];
let args = [...boundArgs, ...ExtraArgs];
//提供 boundThis 作为 this 值,提供 args 为参数调用 target 的 [[Call]] 内部方法,返回结果。
console.log(
boundThis,
...args
);
!function (proto) {
function getContext(context) {
context = context || window;
var type = typeof context;
if (['number', 'string', 'boolean', 'null'].includes(type)) {
context = new context.constructor(context);
}
return context;
}
function call(context, ...args) {
context = getContext(context);
context._fn = this;
let result = context._fn(...args);
delete context._fn;
return result;
}
function apply(context, args) {
context = getContext(context);
context._fn = this;
let result = context._fn(...args);
delete context._fn;
return result;
}
function bind(context, ...bindArgs) {
return (...args) => this.call(context, ...bindArgs, ...args);
}
proto.call = call;
proto.apply = apply;
proto.bind = bind;
}(Function.prototype)
var a = 1;
var aReference = {
base: environmentRecords,
name: 'a',
strict: false
}
var obj = {
name: 'zhufeng',
getName() {
console.log(this.name);
}
}
var getNameReference = {
base: obj,
name: 'getName',
strict: false
};
function two() {
console.log(this);
}
two();
var twoReference = {
base: environmentRecords,
name: 'two',
strict: false
};
方法 | 含义 |
---|---|
GetBase(V) | 返回引用值 V 的基值组件 |
GetReferencedName(V) | 返回引用值 V 的引用名称组件 |
IsStrictReference(V) | 返回引用值 V 的严格引用组件 |
HasPrimitiveBase(V) | 如果基值是 Boolean, String, Number,那么返回 true |
IsPropertyReference(V) | 如果基值是个对象或 HasPrimitiveBase(V) 是 true,那么返回 true;否则返回 false |
IsUnresolvableReference(V) | 如果基值是 undefined 那么返回 true,否则返回 false |
PutValue 中的 V 是原始基值的属性引用时使用下面的 [[Put]] 内部方法。用 base 作为 this 值,用属性 P,值 W,布尔标志 Throw 作为参数调用它。采用以下步骤:
令 O 为 ToObject(base)。
当用属性名 P 调用 O 的 [[Get]] 内部方法,采用以下步骤:
输入类型 | 结果 |
---|---|
Undefined | 抛出 TypeError 异常。 |
Null | 抛出 TypeError 异常。 |
Boolean | 创建一个新的Boolean对象,其 [[PrimitiveValue]]属性被设为该布尔值的值。 |
Number | 创建一个新的Number对象,其[[PrimitiveValue]]属性被设为该数字值。 |
String | 创建一个新的String对象,其 [[PrimitiveValue]] 属性被设为该字符串值。 |
Object | 结果是输入的参数(不转换)。 |
这一算法并不会作用GetValue于执行Expression的结果。这样做的原则是确保delete和typeof这样的运算符可以作用于括号括起来的表达式。
val类型 | 结果 |
---|---|
Undefined | "undefined" |
Null | "null" |
Boolean | "boolean" |
Number | "number" |
String | "string" |
Object(原生,且没有实现 [[call]]) | "object" |
Object(原生或者宿主且实现了 [[call]] | "function" |
Object(宿主且没实现 [[call]]) | 由实现定义,但不能是 "undefined", "boolean", "number", or "string" |
在外面传入的 thisArg 值会修改并成为 this 值。thisArg 是 undefined 或 null 时它会被替换成全局对象,所有其他值会被应用 ToObject 并将结果作为 this 值,这是第三版引入的更改。
在外面传入的 thisArg 值会修改并成为 this 值。thisArg 是 undefined 或 null 时它会被替换成全局对象,所有其他值会被应用 ToObject 并将结果作为 this 值,这是第三版引入的更改。
Function.prototype.bind 创建的函数对象不包含 prototype 属性或 [[Code]], [[FormalParameters]], [[Scope]] 内部属性
var name = "window";
var obj = {
name: "hello",
getName() {
console.log(this.name);
}
};
function getName() {
var objGetName = obj.getName;
objGetName();//window
obj.getName();//hello
(obj.getName)();//hello
(true && obj.getName)();//window
}
getName();
var name = 'window';
var obj = {
name: 'obj',
getName1() {
console.log(this.name)
},
getName2: () => console.log(this.name),
getName3() {
return function () {
console.log(this.name)
}
},
getName4() {
return () => {
console.log(this.name)
}
}
}
var obj2 = { name: 'obj2' }
obj.getName1();//obj
obj.getName1.call(obj2);//obj2
obj.getName2();//window
obj.getName2.call(obj2);//window
obj.getName3()();//window
obj.getName3.call(obj2)();//window
obj.getName3().call(obj2);//obj2
obj.getName4()();//obj
obj.getName4.call(obj2)();//obj2
obj.getName4().call(obj2);//obj